为什么Transformer要用LayerNorm? 您所在的位置:网站首页 mean 怎么用 为什么Transformer要用LayerNorm?

为什么Transformer要用LayerNorm?

#为什么Transformer要用LayerNorm?| 来源: 网络整理| 查看: 265

有关Batch norm和Layer norm的比较可以算上是算法领域的八股文了,为什么BERT不用batch norm而用layer norm的问题都被问烂了,知乎上随便一搜都有很多人讲解BN和LN的区别。通常来说大家都会给这张图:

BN vs LN

大家会说,针对CV和NLP两种问题,这里的三个维度表示的信息不同:

CVNLPNbatch_sizebatch_sizeCchannelseq_lengthH,Wfeature mapdim

如果只看NLP问题,假设我们的batch是(2,3,4)的,也就是batch_size = 2, seq_length = 3, dim = 4的,假设第一个句子是w1 w2 w3,第二个句子是w4 w5 w6,那么这个tensor可以写为:

[[w11, w12, w13, w14], [w21, w22, w23, w24], [w31, w32, w33, w34] [w41, w42, w43, w44], [w51, w52, w53, w54], [w61, w62, w63, w64]]

我们发现,如果是BN的话,会对同一个batch里对应位置上的token求平均值,也就是说 (w11+w12+w13+w14+w41+w42+w43+w44)/8是其中一个mean,一共会求出3个mean,也就是上图里C个(seq_length)个mean。

但是如果是LN的话,看起来是对每个sample里的所有feature求mean,也就是(w11+w12+w13+w14+w21+w22+w23+w24+w31+w32+w33+w34)/12,可以求出一共2个mean,也就是图里N(batch_size)个mean。

我一直对这个计算深信不疑,认为BERT里也是这样的实现,但是有一天我在这个回答看到了 @猛猿 的这个回答:为什么Transformer要用LayerNorm?其中作者给出了两张图:

都是Layer norm但是却不一样

左图和我们认为的LN一致,也是我一直认为的LN,但是右图却是在一个token上求平均,带回我们原来的问题,对于一个(2,3,4)的tensor,(w11+w12+w13+w14)/4是一个mean,一共会有2*3=6个mean。

那到底,BERT里是batch_size个mean(左图的计算方法),还是batch_size*seq_length个mean(右图的计算方法)呢?我们得看看源码。

BERT或者说transformer encoder的pytorch源码比较著名的应该是torch自带的transformer encoder和hugging face自己写的,我们一个个看。

# torch.nn.TransformerEncoderLayer # https://github.com/pytorch/pytorch/blob/master/torch/nn/modules/transformer.py # 412行 self.norm1 = LayerNorm(d_model, eps=layer_norm_eps, **factory_kwargs) # huggingface bert_model # https://github.com/huggingface/transformers/blob/3223d49354e41dfa44649a9829c7b09013ad096e/src/transformers/models/bert/modeling_bert.py#L378 # 382行 self.LayerNorm = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps)

可以看到,无论是火炬自带还是捧着脸复现的transformer encoder或者叫bert layer,里面用的都是torch自己的nn.LayerNorm,并且参数都是对应为768的hidden dimension(变形金刚把它叫做d_model,波特把它叫做hidden_size)。

那我们看看nn.LayerNorm(dim)是一个什么效果,以下代码修改自Understanding torch.nn.LayerNorm in nlp

import torch batch_size, seq_size, dim = 2, 3, 4 embedding = torch.randn(batch_size, seq_size, dim) layer_norm = torch.nn.LayerNorm(dim, elementwise_affine = False) print("y: ", layer_norm(embedding)) eps: float = 0.00001 mean = torch.mean(embedding[:, :, :], dim=(-1), keepdim=True) var = torch.square(embedding[:, :, :] - mean).mean(dim=(-1), keepdim=True) print("mean: ", mean.shape) print("y_custom: ", (embedding[:, :, :] - mean) / torch.sqrt(var + eps))

在以上代码中,我先生成了一个emb,然后使用nn.LayerNorm(dim)计算它layer nrom后的结果,同时,我手动计算了一个在最后一维上的mean(也就是说我的mean的维度是2*3,也就是一共6个mean),如果这样算出来的结果和我调nn.LayerNorm(dim)一致,那就说明,nn.LayerNorm(dim)会给我们(batch_size*seq_length)个mean,也就是刚才上图里右边的方法。计算后结果如下:

y: tensor([[[-0.2500, 1.0848, 0.6808, -1.5156], [-1.1630, -0.7052, 1.3840, 0.4843], [-1.3510, 0.4520, -0.4354, 1.3345]], [[ 0.4372, -0.4610, 1.3527, -1.3290], [ 0.2282, 1.3853, -0.2037, -1.4097], [-0.9960, -0.6184, -0.0059, 1.6203]]]) mean: torch.Size([2, 3, 1]) y_custom: tensor([[[-0.2500, 1.0848, 0.6808, -1.5156], [-1.1630, -0.7052, 1.3840, 0.4843], [-1.3510, 0.4520, -0.4354, 1.3345]], [[ 0.4372, -0.4610, 1.3527, -1.3290], [ 0.2282, 1.3853, -0.2037, -1.4097], [-0.9960, -0.6184, -0.0059, 1.6203]]])

确实一致,也就是说,至少在torch自带和hugging face复现的bert里,layernorm实际上和右图一致是对每个token的feature 单独求mean

那么如果我们想像左图里求出batch_size个mean,怎么用nn.LayerNorm实现呢?只需要修改nn.LayerNorm的参数为nn.LayerNorm([seq_size,dim])即可,代码如下,大家可以跑一下,发现这样和求batch_size个mean是一致的:

import torch batch_size, seq_size, dim = 2, 3, 4 embedding = torch.randn(batch_size, seq_size, dim) layer_norm = torch.nn.LayerNorm([seq_size,dim], elementwise_affine = False) print("y: ", layer_norm(embedding)) eps: float = 0.00001 mean = torch.mean(embedding[:, :, :], dim=(-2,-1), keepdim=True) var = torch.square(embedding[:, :, :] - mean).mean(dim=(-2,-1), keepdim=True) print("mean: ", mean.shape) print("y_custom: ", (embedding[:, :, :] - mean) / torch.sqrt(var + eps))

最后一个问题,按图右这么求,那岂不是和InstanceNorm一样了吗?同样我做了一个代码实验:

from torch.nn import InstanceNorm2d instance_norm = InstanceNorm2d(3, affine=False) x = torch.randn(2, 3, 4) output = instance_norm(x.reshape(2,3,4,1)) #InstanceNorm2D需要(N,C,H,W)的shape作为输入 print(output.reshape(2,3,4)) layer_norm = torch.nn.LayerNorm(4, elementwise_affine = False) print(layer_norm(x))

可以跑一下,发现确实是一致的。

结论:BERT里的layernorm在torch自带的transformer encoder和hugging face复现的bert里,实际上都是在做InstanceNorm。

那么,最开始Vaswani在attention is all you need里提出的使用layernorm是什么呢?tf.tensor2tensor的作者也是Vaswani,那么我认为tf.tensor2tensor应该是符合作者最初的源码设计的,通过翻阅源码(看了无数的文件,大家可以试试,真的很多,各种function封装...),我确认了作者自己的代码里的layernorm使用的参数也是最后一维的dimension,那么也就是说,原作者本质上也是用的InstanceNorm

最后想问问,InstanceNorm是LayerNorm的一种吗?为啥我没看到相关的说法?



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有